package com.yang.controller;

import com.yang.sysc.service.OrderIdGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.CountDownLatch;

/**
 * 分布式锁(竞争锁)
 *
 * @author yanghongjun19
 * @date 2020/12/30
 **/

@RestController
@RequestMapping(value = "/lock")
@Slf4j
public class DistributedLockController {

    @Resource
    private OrderIdGenerator orderIdGenerator;

    @Resource
    private ZooKeeper zooKeeper;

    private static final String PATH = "/locks";
    private static final String SUB_PATH = "/lock";

    private CountDownLatch countDownLatch;
    private String orderId = "";

    @RequestMapping("/orderId")
    public String fetchOrderId() throws Exception {
        countDownLatch = new CountDownLatch(1);
        createOrderId();
        countDownLatch.await();
        return "订单id:" + orderId;
    }

    /**
     * 创建订单
     *
     * @throws Exception 异常
     */
    private void createOrderId() throws Exception {
        orderId = "";
        if (tryLock()) {
            orderId = orderIdGenerator.fetchOrderId();
            log.info("获取到的节点是:" + orderId);
            unLock();
            countDownLatch.countDown();
        } else {
            waitLock();
        }
    }

    /**
     * 获取锁
     *
     * @return 锁是否获取成功
     */
    private boolean tryLock() {
        try {
            zooKeeper.create(PATH + SUB_PATH, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            return true;
        } catch (Exception e) {
            log.info("获取锁失败:" + e.getMessage());
            return false;
        }
    }

    /**
     * 等待锁
     */
    private void waitLock() throws Exception {
        zooKeeper.getChildren(PATH, w -> {
            log.info("节点改变事件" + w.getType());
            if (w.getType() == Watcher.Event.EventType.NodeChildrenChanged) {
                try {
                    createOrderId();
                } catch (Exception e) {
                    log.info("创建节点失败:" + e.getMessage());
                }
            }
        });
    }

    /**
     * 释放锁
     */
    private void unLock() throws Exception {
        Stat stat = zooKeeper.exists(PATH + SUB_PATH, false);
        if (stat != null) {
            zooKeeper.delete(PATH + SUB_PATH, stat.getVersion());
        }
    }
}
